package com.btm.qiaoqiang.ruleadp.parser;

import com.btm.qiaoqiang.ruleadp.CustomDynamicComparator;
import com.btm.qiaoqiang.ruleadp.EnumKeyChar;
import org.springframework.util.StringUtils;

import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 规则表达式解析器
 *
 * @author: btm
 * @time: 14:01 2020/9/15
 **/
public class RuleExpressionParser {

    private ThreadLocal<RuleExpression> ruleExpressionThreadLocal = new ThreadLocal<>();
    private static final char TRUE_CHAR_EXPRESSION_FLAG = EnumKeyChar.PLUS.getChar();
    private static final char FALSE_CHAR_EXPRESSION_FLAG = EnumKeyChar.REDUCE.getChar();
    private final CustomDynamicComparator customDynamicComparator;

    public RuleExpressionParser() {
        this(null);
    }

    public RuleExpressionParser(CustomDynamicComparator customDynamicComparator) {
        this.customDynamicComparator = customDynamicComparator;
    }

    /**
     * 解析表达式，将表达式解析为规则<br/>
     * 检测完后，一定使用{@link RuleExpressionParser#finish()}方法清理数据
     *
     * @param expression 表达式
     */
    public void parser(String expression) {
        if (!StringUtils.hasText(expression)) {
            throw new IllegalArgumentException("illegal expression");
        }
        System.out.println("规则表达式："+expression);
        expression = preArrangement(expression);
        System.out.println("优化整理后规则表达式："+expression);
        RuleExpression ruleExpression = doParser(expression, 0, expression.length() - 1, customDynamicComparator);
        ruleExpression.cleanVirtualNode();
        ruleExpressionThreadLocal.set(ruleExpression);
        //从头开始解析
    }

    public RuleExpression getRuleExpression() {
        return ruleExpressionThreadLocal.get();
    }

    /**
     * 全部校验完毕后需调用此方法
     */
    public void finish() {
        ruleExpressionThreadLocal.remove();
    }

    /**
     * 解析处理前对表达式进行优化整理
     * @param expression 表达式正文
     * @return 优化整理后的表达式正文
     */
    private String preArrangement(String expression) {
        //优化整理规则表达式中的花括号
        String curlyBracketRegex = "@[A-Za-z0-9]+\\{[A-Za-z0-9,]+\\}";
        Pattern pattern = Pattern.compile(curlyBracketRegex);
        do {
            Matcher matcher = pattern.matcher(expression);
            if (!matcher.find()) {
                break;
            }
            String regexStr = matcher.group();
            expression = expression.replace(regexStr, parserArrangement(regexStr));
        } while (true);
        //优化整理后的规则表达式
        return expression;
    }

    /**
     * 将花括号形式转换为｜符号连接模式
     * @param arrangement 花括号表达式
     * @return
     */
    private String parserArrangement(String arrangement) {
        int end = arrangement.indexOf('{');
        String valuePrefix = arrangement.substring(0,end)+"=";
        String[] valueArr = arrangement.substring(end+1,arrangement.length()-1).split(",");
        for (int i = 0;i<valueArr.length;i++) {
            valueArr[i] = valuePrefix+valueArr[i];
        }
        return "(" + StringUtils.arrayToDelimitedString(valueArr, "|") +")";
    }

    /**
     * 解析表达式<br/>
     * 存在递归调用
     * @param expression       表达式正文
     * @param continueIndex 规则表达式已经解析到的位置
     * @param expressionEndIndex 要解析的表达式的末尾下标
     */
    private RuleExpression doParser(String expression, int continueIndex, int expressionEndIndex, CustomDynamicComparator customDynamicComparator) {
        RuleExpression ruleExpression = new RuleExpression(customDynamicComparator);
        ruleExpression.setNextChar(continueIndex);
        while (ruleExpression.getCharIndex() <= expressionEndIndex) {
            char oneChar = expression.charAt(ruleExpression.getCharIndex());
            switch (EnumKeyChar.getKeyCharByChar(oneChar)) {
                case AT:
                    //后续读取到的字符或数字为变量名、比较关系、给定值
                    ruleExpression.nextChar();
                    doParserMethodNameAndValue(expression, ruleExpression, expressionEndIndex);
                    break;
                case AND:
                    //组合关系，对应规则树的父子节点关系
                    ruleExpression.nextChar();
                    if (Objects.isNull(ruleExpression.getTempNode())) {
                        ruleExpression.rememberCurrentNo();
                    }
                    break;
                case OR:
                    //并列关系，对应规则树的兄弟节点关系
                    if (Objects.nonNull(ruleExpression.getTempNode())) {
                        ruleExpression.currentToTempNode();
                    }
                    ruleExpression.nextChar();
                    ruleExpression.currentToSupperNode();
                    break;
                case LEFT_BRACKET:
                    //层级关系，对应规则树的父子节点关系，处理内容直到读取到)为止
                    ruleExpression.nextChar();
                    RuleExpression splicingRuleExpression = doParser(expression, ruleExpression.getCharIndex(), expressionEndIndex, customDynamicComparator);
                    ruleExpression.splicingRuleExpression(splicingRuleExpression);
                    break;
                case RIGHT_BRACKET:
                    //层级关系结束，将当前节点指针指向父节点
                    ruleExpression.nextChar();
                    ruleExpression.currentToRootNode();
                    return ruleExpression;
                case DEFAULT:
                default:
                    //读取到无法处理的字符
                    throw new RuntimeException("Illegal symbol ["+oneChar+"]");
            }
        }
        return ruleExpression;
    }

    /**
     * 读取单个标的值
     * @param expression 表达式
     * @param ruleExpression 规则表达式
     * @param force true:被强制转义，此后的第一个字符被作为普通字符串处理
     */
    private void readAloneValue(String expression, RuleExpression ruleExpression, boolean force) {
        if (!force && TRUE_CHAR_EXPRESSION_FLAG==expression.charAt(ruleExpression.getCharIndex())) {
            ruleExpression.setCurrentNodeValue(Boolean.TRUE);
            ruleExpression.nextChar();
        } else if (!force && FALSE_CHAR_EXPRESSION_FLAG==expression.charAt(ruleExpression.getCharIndex())) {
            ruleExpression.setCurrentNodeValue(Boolean.FALSE);
            ruleExpression.nextChar();
        } else {
            boolean isCustomDynamicValue = false;
            String value = readOneString(expression, ruleExpression, force);
            if (Objects.nonNull(this.customDynamicComparator)) {
                isCustomDynamicValue = true;
            }
            ruleExpression.setCurrentNodeValue(value, isCustomDynamicValue);
        }
    }

    /**
     * 解析取值方法名和给定值
     * @param expression 表达式
     * @param ruleExpression 规则表达式
     * @param expressionEndIndex 要解析的表达式的末尾下标
     */
    private void doParserMethodNameAndValue(String expression, RuleExpression ruleExpression, int expressionEndIndex) {
        ruleExpression.createNullChildNode(readFieldName(expression, ruleExpression));
        boolean gt = false;
        boolean lt = false;
        boolean notEq = false;
        boolean force = false;
        while (ruleExpression.getCharIndex() <= expressionEndIndex) {
            char oneChar = expression.charAt(ruleExpression.getCharIndex());
            switch (EnumKeyChar.getKeyCharByChar(oneChar)) {
                case EQUALS:
                    if (gt) {
                        //大于等于，后续读取到的字符或数字即为标的值，直到读取到非字符或非数字
                        ruleExpression.setCurrentNodeGte();
                        gt = false;
                    } else if (lt) {
                        //小于等于，后续读取到的数字即为标的值，直到读取到非数字
                        ruleExpression.setCurrentNodeLte();
                        lt = false;
                    } else if (notEq) {
                        ruleExpression.setCurrentNodeNotEqual();
                        notEq = false;
                    } else {
                        //等于，后续读取到的数字即为标的值，直到读取到非数字
                        ruleExpression.setCurrentNodeEqual();
                    }
                    ruleExpression.nextChar();
                    break;
                case GREATER_THAN:
                    //大于，后续读取到的数字即为标的值，直到读取到非数字
                    gt = true;
                    ruleExpression.nextChar();
                    break;
                case LESS_THAN:
                    //小于，后续读取到的数字即为标的值，直到读取到非数字
                    lt = true;
                    ruleExpression.nextChar();
                    break;
                case NOT:
                    //不等于，后续读取到的数字即为标的值，直到读取到非数字
                    notEq = true;
                    ruleExpression.nextChar();
                    break;
                case TRANSLATION:
                    //此case分支必须作为default分支钱的最后一个分支
                    //转义符，后面读取到的第一个字符作为普通字符处理，不作为特殊标记字符处理
                    force = true;
                    ruleExpression.nextChar();
                case DEFAULT:
                default:
                    if (!force && EnumKeyChar.isSplitKeyChar(oneChar)) {
                        return;
                    }
                    //处理标的值，只处理字母或数字
                    if (gt) {
                        ruleExpression.setCurrentNodeGt();
                        gt = false;
                    } else if (lt) {
                        ruleExpression.setCurrentNodeLt();
                        lt = false;
                    } else if (notEq) {
                        ruleExpression.setCurrentNodeNotEqual();
                        notEq = false;
                    }
                    readAloneValue(expression, ruleExpression, force);
                    force = false;
            }
        }
    }

    /**
     * 读取取值方法名
     * @param expression 表达式正文
     * @param ruleExpression 规则表达式
     * @return 取值方法名
     */
    private String readFieldName(String expression, RuleExpression ruleExpression) {
        return readOneString(expression, ruleExpression, false);
    }

    /**
     * 从charIndex开始读取所有的字母、数字，组成一个字符串
     * @param expression 表达式正文
     * @param ruleExpression 规则表达式
     * @param force 是否将下一个字符作为一个普通字符
     * @return 读取到的字符串
     */
    private String readOneString(String expression, RuleExpression ruleExpression, boolean force) {
        int variableNameStartCharIndex = ruleExpression.getCharIndex();
        int expressionLength = expression.length();
        for (; ruleExpression.getCharIndex() < expressionLength; ruleExpression.nextChar()) {
            char currentChar = expression.charAt(ruleExpression.getCharIndex());
            if (!force && EnumKeyChar.TRANSLATION.equals(currentChar)) {
                force = true;
                continue;
            }
            // 当前字符不被强制转义且是系统使用的特殊字符，则说明前面的字符是一个完成的字符串，可以直接返回
            if (!force && EnumKeyChar.isKeyChar(currentChar)) {
                break;
            } else {
                force = false;
            }
        }
        if (ruleExpression.getCharIndex() <= variableNameStartCharIndex) {
            throw new RuntimeException("@ is variable symbol, but not found variable after index ["+variableNameStartCharIndex+"]");
        }
        return expression.substring(variableNameStartCharIndex,ruleExpression.getCharIndex());
    }


}
